#include "av.h"

extern
int lua_new_codec_context(lua_State * L) {
	lua_push_codec_context(L, avcodec_alloc_context());
	return 1;
}

static
int lua_av_codec_context_open(lua_State *L) {
	AVCodecContext * ctx;
	AVCodec * codec;
	lua_av_object * o = lua_check_codec_context(L, 1, &ctx);
	lua_check_codec(L, 2, &codec);

	lua_object_attach(L, o, 2);

	int result = avcodec_open(ctx, codec);
	if (result < 0) {
		luaL_error(L, "error opening codec  (%d)", result);
	}
	return 0;
}

static
int lua_av_codec_context_close(lua_State *L) {
	AVCodecContext * ctx;
	lua_check_codec_context(L, 1, &ctx);
	avcodec_close(ctx);
	return 0;
}

static
int lua_av_codec_context_decode_video(lua_State *L) {
	size_t len;
	AVCodecContext * ctx;
	AVFrame * frame;
	lua_check_codec_context(L, 1, &ctx);
	const uint8_t *buf = (const uint8_t *) lua_tolstring(L, 2, &len);
	lua_check_frame(L, 3, &frame);
	int got_picture = 0;

	int error = avcodec_decode_video(ctx, frame, &got_picture,
		buf, len);

	if (error < 0) {
		luaL_error(L, "error (%d) while decoding video frame", error);
		return 0;
	}
	lua_pushboolean(L, got_picture);
	return 1;
}

static
int lua_av_codec_context_decode_audio(lua_State *L) {
	AVCodecContext * ctx;
	lua_check_codec_context(L, 1, &ctx);
	luaL_error(L, "not yet implemented");
	return 0;
}

static
int lua_av_codec_context_encode_audio(lua_State *L) {
	AVCodecContext * ctx;
	lua_check_codec_context(L, 1, &ctx);
	luaL_error(L, "not yet implemented");
	return 0;
}

static
int lua_av_codec_context_encode_video(lua_State *L) {
	AVCodecContext * ctx;
	AVFrame * frame;
	lua_check_codec_context(L, 1, &ctx);
	lua_check_frame(L, 2, &frame);
	int video_outbuf_size = 200000;
	void *video_outbuf = av_malloc(video_outbuf_size);

	int length = avcodec_encode_video(ctx, video_outbuf, video_outbuf_size, frame);
	if (length < 0) {
		luaL_error(L, "error (%d) while encoding video", length);
		return 0;
	}

	if (length > 0) {
		AVPacket * pkt = av_mallocz(sizeof(AVPacket));
		if(!pkt) {
			luaL_error(L, "allocation error");
			return 0;
		}
		av_new_packet(pkt, length);
		memcpy(pkt->data, video_outbuf, length);
		av_free(video_outbuf);

		lua_push_packet(L, pkt);
		return 1;
	}

	return 0;
}

static
const struct luaL_reg av_codec_context_reg[] = {
	{"open", lua_av_codec_context_open},
	{"decode_video", lua_av_codec_context_decode_video},
	{"decode_audio", lua_av_codec_context_decode_audio},
	{"encode_audio", lua_av_codec_context_encode_audio},
	{"encode_video", lua_av_codec_context_encode_video},
	{"close", lua_av_codec_context_close},
	{NULL, NULL},
};

static
void lua_push_rational(lua_State * L, AVRational q) {
	lua_pushnumber(L, av_q2d(q));
}

static
AVRational lua_check_rational(lua_State * L, int narg) {
	lua_Number n = lua_tonumber(L, narg);
	return av_d2q(n, 1000);
}

lua_constant codec_flag_consts[] = {
	{ "obmc", CODEC_FLAG_OBMC },
	{ "qscale", CODEC_FLAG_QSCALE },
	{ "4mv", CODEC_FLAG_4MV },
	{ "qpel", CODEC_FLAG_QPEL },
	{ "gmc", CODEC_FLAG_GMC },
	{ "mv0", CODEC_FLAG_MV0 },
	{ "part", CODEC_FLAG_PART },
	{ "input_preserved", CODEC_FLAG_INPUT_PRESERVED },
	{ "pass1", CODEC_FLAG_PASS1 },
	{ "pass2", CODEC_FLAG_PASS2 },
	{ "loop_filter", CODEC_FLAG_LOOP_FILTER },
	{ "extern_huff", CODEC_FLAG_EXTERN_HUFF },
	{ "gray", CODEC_FLAG_GRAY },
	{ "emu_edge", CODEC_FLAG_EMU_EDGE },
	{ "psnr", CODEC_FLAG_PSNR },
	{ "truncated", CODEC_FLAG_TRUNCATED },
	{ "normalize_aqp", CODEC_FLAG_NORMALIZE_AQP },
	{ "interlaced_dct", CODEC_FLAG_INTERLACED_DCT },
	{ "low_delay", CODEC_FLAG_LOW_DELAY },
	{ "alt_scan", CODEC_FLAG_ALT_SCAN },
	{ "global_header", CODEC_FLAG_GLOBAL_HEADER },
	{ "bitexact", CODEC_FLAG_BITEXACT },
	{ "ac_pred", CODEC_FLAG_AC_PRED },
	{ "h263p_umv", CODEC_FLAG_H263P_UMV },
	{ "cbp_rd", CODEC_FLAG_CBP_RD },
	{ "qp_rd", CODEC_FLAG_QP_RD },
	{ "h263p_aiv", CODEC_FLAG_H263P_AIV },
	{ "h263p_slice_struct", CODEC_FLAG_H263P_SLICE_STRUCT },
	{ "interlaced_me", CODEC_FLAG_INTERLACED_ME },
	{ "svcd_scan_offset", CODEC_FLAG_SVCD_SCAN_OFFSET },
	{ "closed_gop", CODEC_FLAG_CLOSED_GOP },
	{ NULL, 0 },
};

static
void lua_push_codec_flag(lua_State * L, int flags) {
	lua_push_flag(L, flags, codec_flag_consts);
}

DEFINE_TYPE_STRUCT(codec_context, AVCodecContext,
	LUA_OBJECT_CODEC_CONTEXT_ID, av_free, av_codec_context_reg)

DEFINE_INDEX_HEADER(codec_context, AVCodecContext)
	HANDLE_INDEX_ENTRY(codec_id, lua_push_codec_id);
	HANDLE_INDEX_ENTRY(codec_type, lua_push_codec_type);
	HANDLE_INDEX_ENTRY(codec_name, lua_pushstring);
	HANDLE_INDEX_ENTRY(bit_rate, lua_pushinteger);
	HANDLE_INDEX_ENTRY(bit_rate_tolerance, lua_pushinteger);
	HANDLE_INDEX_ENTRY(flags, lua_push_codec_flag);
	HANDLE_INDEX_ENTRY(sub_id, lua_pushinteger);
	HANDLE_INDEX_ENTRY(me_method, lua_push_me_method);
	HANDLE_INDEX_ENTRY(time_base, lua_push_rational);
	switch (object->codec_type) {
		case CODEC_TYPE_VIDEO:
			HANDLE_INDEX_ENTRY(width, lua_pushinteger);
			HANDLE_INDEX_ENTRY(height, lua_pushinteger);
			HANDLE_INDEX_ENTRY(gop_size, lua_pushinteger);
			HANDLE_INDEX_ENTRY(pix_fmt, lua_push_pixel_format);
			HANDLE_INDEX_ENTRY(rate_emu, lua_pushinteger);
			break;
		case CODEC_TYPE_AUDIO:
			HANDLE_INDEX_ENTRY(sample_rate, lua_pushinteger);
			HANDLE_INDEX_ENTRY(channels, lua_pushinteger);
			HANDLE_INDEX_ENTRY(sample_fmt, lua_push_sample_format);
			break;
		default:
			break;
	}
DEFINE_INDEX_TRAILER()

DEFINE_NEWINDEX_HEADER(codec_context, AVCodecContext)
	HANDLE_NEWINDEX_ENTRY(codec_id, lua_check_codec_id);
	HANDLE_NEWINDEX_ENTRY(codec_type, lua_check_codec_type);
	// HANDLE_NEWINDEX_ENTRY(codec_name, lua_checkstring);
	HANDLE_NEWINDEX_ENTRY(bit_rate, luaL_checkinteger);
	HANDLE_NEWINDEX_ENTRY(bit_rate_tolerance, luaL_checkinteger);
	HANDLE_NEWINDEX_ENTRY(flags, luaL_checkinteger);
	HANDLE_NEWINDEX_ENTRY(sub_id, luaL_checkinteger);
	HANDLE_NEWINDEX_ENTRY(me_method, lua_check_me_method);
	HANDLE_NEWINDEX_ENTRY(time_base, lua_check_rational);
	switch (object->codec_type) {
		case CODEC_TYPE_VIDEO:
			HANDLE_NEWINDEX_ENTRY(width, luaL_checkinteger);
			HANDLE_NEWINDEX_ENTRY(height, luaL_checkinteger);
			HANDLE_NEWINDEX_ENTRY(gop_size,luaL_checkinteger);
			HANDLE_NEWINDEX_ENTRY(pix_fmt, lua_check_pixel_format);
			HANDLE_NEWINDEX_ENTRY(rate_emu, luaL_checkinteger);
			break;
		case CODEC_TYPE_AUDIO:
			HANDLE_NEWINDEX_ENTRY(sample_rate, luaL_checkinteger);
			HANDLE_NEWINDEX_ENTRY(channels, luaL_checkinteger);
			HANDLE_NEWINDEX_ENTRY(sample_fmt, lua_check_sample_format);
			break;
		default:
			break;
	}
DEFINE_NEWINDEX_TRAILER()

DEFINE_OBJECT_FUNCTIONS(codec_context, AVCodecContext)
